Skip to content

fix(installer): use relative paths for hook commands in settings.local.json#7

Open
riaworks wants to merge 1 commit into
mainfrom
fix/hook-settings-path-simplification
Open

fix(installer): use relative paths for hook commands in settings.local.json#7
riaworks wants to merge 1 commit into
mainfrom
fix/hook-settings-path-simplification

Conversation

@riaworks

@riaworks riaworks commented Mar 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replace platform-specific hook command paths with simple relative paths (node .claude/hooks/<file>)
  • Remove hardcoded timeout: 10 from HOOK_EVENT_MAP and generated settings.local.json
  • Remove unused isWindows and hookFilePath variables

Problem

The installer generated different hook commands per platform:

Platform Generated command Issue
Windows node "C:\path\to\hooks\synapse-engine.cjs" Machine-dependent absolute path with escaped backslashes
Unix node "$CLAUDE_PROJECT_DIR/.claude/hooks/synapse-engine.cjs" $CLAUDE_PROJECT_DIR has known bugs (GH #6023/#5814)

Both approaches caused UserPromptSubmit hook error in installed projects. The timeout: 10 override was also unnecessary — Claude Code manages hook timeouts natively (default 60s), and each hook already has its own internal safety timeout.

Fix

Single relative path for all platforms:

{
  "type": "command",
  "command": "node .claude/hooks/synapse-engine.cjs"
}

Claude Code resolves relative paths from the project root (cwd), so this works everywhere.

Test plan

  • All 16 existing tests pass (artifact-copy-pipeline.test.js)
  • HOOK_EVENT_MAP still correctly routes precompact → PreCompact (MIS-3.1)
  • Verified manually: hook runs without error in 3 test projects (Windows 11)
  • Verify on macOS/Linux that relative paths resolve correctly
  • Fresh install test: npx aios-core install generates correct settings.local.json

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Simplified hook configuration management by removing per-hook timeout overrides.
    • Standardized hook command path handling across platforms using relative paths.
    • Reduced configuration complexity in IDE settings.

…l.json

The installer generated platform-specific hook commands that caused issues:
- Windows: absolute paths with escaped backslashes (machine-dependent, fragile)
- Unix: $CLAUDE_PROJECT_DIR variable (known bugs GH #6023/#5814)

Both approaches led to UserPromptSubmit hook errors in installed projects.

Changes:
- Use relative path `node .claude/hooks/<file>` on all platforms
- Remove hardcoded `timeout: 10` from HOOK_EVENT_MAP and generated settings
  (Claude Code manages hook timeouts natively, each hook has internal safety)
- Remove unused `isWindows` and `hookFilePath` variables
- Update tests to reflect timeout removal

The HOOK_EVENT_MAP event routing (MIS-3.1) remains unchanged — precompact
is correctly mapped to PreCompact, not UserPromptSubmit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 2, 2026

Copy link
Copy Markdown

Walkthrough

Timeout configuration removed from hook settings to enable external management by Claude Code. Hook command paths changed from Windows-specific absolute paths to relative paths using the format node .claude/hooks/<hookFileName>. Related tests updated to expect undefined timeout values.

Changes

Cohort / File(s) Summary
Hook Configuration Timeout Removal
packages/installer/src/wizard/ide-config-generator.js, packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js
Removed timeout field from HOOK_EVENT_MAP entries and DEFAULT_HOOK_CONFIG object. Hook command paths updated to use relative path format (node .claude/hooks/<hookFileName>) instead of conditional Windows-specific absolute paths. Eliminated Windows path escaping logic and associated guards. Test assertions updated to expect config.timeout as undefined.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main change: replacing platform-specific paths with relative paths for hook commands in settings.local.json.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/hook-settings-path-simplification

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Mar 2, 2026

Copy link
Copy Markdown

📊 Coverage Report

Coverage report not available

📈 Full coverage report available in Codecov


Generated by PR Automation (Story 6.1)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js (1)

177-177: Add exact command-path assertions to lock the PR’s core behavior.

Nice update on timeout removal. One gap: these tests still don’t assert the exact hook command format (node .claude/hooks/<file>), so an absolute-path regression could still pass.

Suggested test hardening
         const upsCommands = settings.hooks.UserPromptSubmit.flatMap(e => e.hooks.map(h => h.command));
         expect(upsCommands.some(c => c.includes('synapse-engine'))).toBe(true);
+        expect(upsCommands).toContain('node .claude/hooks/synapse-engine.cjs');

         // code-intel-pretool → PreToolUse with matcher
         expect(settings.hooks.PreToolUse).toBeDefined();
@@
         const ptuCommands = settings.hooks.PreToolUse.flatMap(e => e.hooks.map(h => h.command));
         expect(ptuCommands.some(c => c.includes('code-intel-pretool'))).toBe(true);
+        expect(ptuCommands).toContain('node .claude/hooks/code-intel-pretool.cjs');

@@
         const pcCommands = settings.hooks.PreCompact.flatMap(e => e.hooks.map(h => h.command));
         expect(pcCommands.some(c => c.includes('precompact-session-digest'))).toBe(true);
+        expect(pcCommands).toContain('node .claude/hooks/precompact-session-digest.cjs');

Also applies to: 185-185, 193-193, 207-207

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js`
at line 177, The test currently only checks timeout removal with
expect(config.timeout).toBeUndefined(); strengthen it by adding exact
command-path assertions for the hook(s): locate the same test block(s) where
config is inspected (the lines containing
expect(config.timeout).toBeUndefined()) and add assertions that the hook command
string(s) equal the exact expected format "node .claude/hooks/<filename>" (for
whichever property holds the hook, e.g., config.command or
config.hooks[...].command); apply the same exact-path assertion to the other
occurrences mentioned (the blocks around the other
expect(config.timeout).toBeUndefined() checks at the same test file) so the test
fails on any absolute-path regression.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/installer/src/wizard/ide-config-generator.js`:
- Around line 791-795: The current construction of hookCommand interpolates
hookFileName directly and is vulnerable to spaces or shell metacharacters;
instead sanitize or avoid the shell: validate hookFileName against a safe
pattern (e.g., allow only alphanumerics, dots, dashes, underscores) and/or
escape quotes/metacharacters in hookFileName, then either wrap the sanitized
name in quotes when building hookCommand or—preferably—stop using a single shell
string and invoke node with an argv array (e.g., use
child_process.spawn/execFile with arguments ['.claude/hooks/<sanitizedName>'])
so the value of hookFileName cannot be interpreted by the shell. Ensure changes
target the hookCommand construction that uses hookFileName.

---

Nitpick comments:
In
`@packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js`:
- Line 177: The test currently only checks timeout removal with
expect(config.timeout).toBeUndefined(); strengthen it by adding exact
command-path assertions for the hook(s): locate the same test block(s) where
config is inspected (the lines containing
expect(config.timeout).toBeUndefined()) and add assertions that the hook command
string(s) equal the exact expected format "node .claude/hooks/<filename>" (for
whichever property holds the hook, e.g., config.command or
config.hooks[...].command); apply the same exact-path assertion to the other
occurrences mentioned (the blocks around the other
expect(config.timeout).toBeUndefined() checks at the same test file) so the test
fails on any absolute-path regression.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41aa9a9 and d1f22fd.

📒 Files selected for processing (2)
  • packages/installer/src/wizard/ide-config-generator.js
  • packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js

Comment on lines +791 to 795
// Use relative path — works on all platforms, no $CLAUDE_PROJECT_DIR bugs
// (GH #6023/#5814), no fragile absolute Windows paths with escaped backslashes.
// Claude Code resolves relative paths from the project root (cwd).
const hookCommand = `node .claude/hooks/${hookFileName}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Quote and sanitize hook filenames before building shell commands.

On Line 794, hookFileName is interpolated directly into a shell command. Filenames with spaces or shell metacharacters can break execution or cause command injection.

Proposed fix
-  const hookFiles = allFiles.filter(f => f.endsWith('.cjs'));
+  const SAFE_HOOK_FILE_RE = /^[A-Za-z0-9._-]+\.cjs$/;
+  const hookFiles = allFiles.filter((f) => SAFE_HOOK_FILE_RE.test(f));

@@
-    const hookCommand = `node .claude/hooks/${hookFileName}`;
+    const hookCommand = `node ".claude/hooks/${hookFileName}"`;

As per coding guidelines, "Look for potential security vulnerabilities."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/installer/src/wizard/ide-config-generator.js` around lines 791 -
795, The current construction of hookCommand interpolates hookFileName directly
and is vulnerable to spaces or shell metacharacters; instead sanitize or avoid
the shell: validate hookFileName against a safe pattern (e.g., allow only
alphanumerics, dots, dashes, underscores) and/or escape quotes/metacharacters in
hookFileName, then either wrap the sanitized name in quotes when building
hookCommand or—preferably—stop using a single shell string and invoke node with
an argv array (e.g., use child_process.spawn/execFile with arguments
['.claude/hooks/<sanitizedName>']) so the value of hookFileName cannot be
interpreted by the shell. Ensure changes target the hookCommand construction
that uses hookFileName.

@github-actions

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had
recent activity. Please update it or it may be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant